///////////////////////////////////////////////////////////////////////////////
//
// lighthouse3D - 3D Software Library
//
// File: CBMPFileHandler.cpp
//
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2003-2013 Marcus Reinhardt
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
///////////////////////////////////////////////////////////////////////////////
//
// You can find a copy of the GNU General Public License (gpl.txt) in the
// directory of the library.
//
///////////////////////////////////////////////////////////////////////////////
#include "CBMPFileHandler.h"

///////////////////////////////////////////////////////////////////////////////
// CBMPFileHandler::CBMPFileHandler( ... )
///////////////////////////////////////////////////////////////////////////////
CBMPFileHandler::CBMPFileHandler()
{
}

///////////////////////////////////////////////////////////////////////////////
// CBMPFileHandler::~CBMPFileHandler( ... )
///////////////////////////////////////////////////////////////////////////////
CBMPFileHandler::~CBMPFileHandler()
{
}

///////////////////////////////////////////////////////////////////////////////
// CBMPFileHandler::loadBMPFile()( ... )
///////////////////////////////////////////////////////////////////////////////
// Loads a BMP File
//
// Limitations
// * only 24-Bit data
// * only positive height
///////////////////////////////////////////////////////////////////////////////
bool CBMPFileHandler::loadBMPFile(const char *fname)
{
  FILE *f;
  int counter;
  unsigned int padding, offset;

  TBMPHeader BMPHeader;
  TBMPInfoHeader BMPInfoHeader;

  f = fopen(fname, "rb");

  if( f == NULL )
  {
	 return false;
  }

  // Try to read the header of the BMP file
  if( fread(&BMPHeader.bmpType, sizeof(BMPHeader.bmpType), 1, f) == 0 )
  {
	 if(f != NULL)
	 {
		fclose(f);
	 }

	 return false;
  }

  // Header validation
  if ( BMPHeader.bmpType != 'MB' )
  {
	 fclose(f);

	 return false;
  }

  // Load the header step by step!!!
  fread(&BMPHeader.bmpFileSize, sizeof(BMPHeader.bmpFileSize), 1, f);
  fread(&BMPHeader.bmpReserved1, sizeof(BMPHeader.bmpReserved1), 1, f);
  fread(&BMPHeader.bmpReserved1, sizeof(BMPHeader.bmpReserved1), 1, f);
  fread(&BMPHeader.bmpDataOffset, sizeof(BMPHeader.bmpDataOffset), 1, f);

  // Load the infoheader step by step!!!
  fread(&BMPInfoHeader.bmpHeaderSize, sizeof(BMPInfoHeader.bmpHeaderSize), 1, f);
  fread(&BMPInfoHeader.bmpWidth, sizeof(BMPInfoHeader.bmpWidth), 1, f);
  fread(&BMPInfoHeader.bmpHeight, sizeof(BMPInfoHeader.bmpHeight), 1, f);
  fread(&BMPInfoHeader.bmpPlanes, sizeof(BMPInfoHeader.bmpPlanes), 1, f);
  fread(&BMPInfoHeader.bmpBPP, sizeof(BMPInfoHeader.bmpBPP), 1, f);
  fread(&BMPInfoHeader.bmpCompression, sizeof(BMPInfoHeader.bmpCompression), 1, f);
  fread(&BMPInfoHeader.bmpImageSize, sizeof(BMPInfoHeader.bmpImageSize), 1, f);
  fread(&BMPInfoHeader.bmpXResolution, sizeof(BMPInfoHeader.bmpXResolution), 1, f);
  fread(&BMPInfoHeader.bmpYResolution, sizeof(BMPInfoHeader.bmpYResolution), 1, f);
  fread(&BMPInfoHeader.bmpNColors, sizeof(BMPInfoHeader.bmpNColors), 1, f);
  fread(&BMPInfoHeader.bmpImportantColors, sizeof(BMPInfoHeader.bmpImportantColors), 1, f);

  // Header validation
  if ( BMPHeader.bmpType != 'MB' )
  {
	 fclose(f);
	 return false;
  }
  if ( BMPInfoHeader.bmpBPP != 24 )
  {
	 fclose(f);
	 return false;
  }

  if ( BMPInfoHeader.bmpCompression != 0 )
  {
     fclose(f);
     return false;
  }
  
  if ( BMPInfoHeader.bmpNColors != 0 )
  {
     fclose(f);
     return false;
  }
  
  if ( BMPInfoHeader.bmpImageSize == 0 )
  {
     BMPInfoHeader.bmpImageSize = (BMPInfoHeader.bmpWidth *
                                   BMPInfoHeader.bmpBPP + 7) / 8 *
    	                           BMPInfoHeader.bmpHeight; 
    	                           
     if (BMPInfoHeader.bmpImageSize <= 0)
     {
        fclose(f);
        return false;
     }
  }
  
  picData.sizeX = BMPInfoHeader.bmpWidth;
  picData.sizeY = BMPInfoHeader.bmpHeight;

  padding = (int) ( (float)BMPInfoHeader.bmpWidth*(float)BMPInfoHeader.bmpBPP/ 8 );
  while( (padding % 4) != 0)
  {
      padding++;
  }

  if (BMPInfoHeader.bmpHeight < 0)
  {
      fclose(f);
      return false;
  }

  // fseek(f, BMPHeader.bmpDataOffset, SEEK_SET);

  if( fread(&picData.raw, 1, BMPInfoHeader.bmpImageSize, f) != BMPInfoHeader.bmpImageSize)
  {
     fclose(f);
         
     return false;
  }

  // Convert the data from bgr to rgb 
  counter = 0;
  offset = padding - BMPInfoHeader.bmpWidth*BMPInfoHeader.bmpBPP/8;
  for (unsigned int i = 0; i < BMPInfoHeader.bmpImageSize; i += BMPInfoHeader.bmpBPP/8)
  {

       if (((i-1) % padding) == 0)
           i+=offset;

	   picData.rgb[counter] = picData.raw[i+2] + picData.raw[i+1]*256 + picData.raw[i]*256*256;
       counter++;

  }
  
  fclose(f);

  if ( counter == 0 )
      return false;
  
  return true;
}

///////////////////////////////////////////////////////////////////////////////
// CBMPFileHandler::saveBMPFile()( ... )
///////////////////////////////////////////////////////////////////////////////
// Save a BMP File
//
// Unimplemented
///////////////////////////////////////////////////////////////////////////////
bool CBMPFileHandler::saveBMPFile(const char *fname, TPictureData *p)
{
  return false;
}